// @coding: utf8

#include <iostream>
#include <cstdint>
#include <vector>
#include <algorithm>
#include <cassert>
#include <ctime>
#include <random>
#include <chrono>

/**
 * 这个应该也是有三种做法：
 * 1.wild牌直接暴力枚举，然后用非癞子的算法算。前面已经写过了，就不写了。
 * 2.牌型检测过程中进行wild补充分析
 * 3.查表法，其实也是一种暴力解法，不实现这种了。
 */


int ALL_CARDS[] = {
    0x102,0x103,0x104,0x105,0x106,0x107,0x108,0x109,0x10a,0x10b,0x10c,0x10d,0x10e, //分别代表方块2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x202,0x203,0x204,0x205,0x206,0x207,0x208,0x209,0x20a,0x20b,0x20c,0x20d,0x20e, //分别代表梅花2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x302,0x303,0x304,0x305,0x306,0x307,0x308,0x309,0x30a,0x30b,0x30c,0x30d,0x30e, //分别代表红桃2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40a,0x40b,0x40c,0x40d,0x40e, //分别代表黑桃2,3,4,5,6,7,8,9,10,J,Q,K,A]
    0x50f, //代表小王
    0x610, //代表大王
};

// 花色
enum ECardColor : int
{
    kHeart = 1,
    kSpade = 2,
    kClub = 3,
    kDiamond = 4,
};

// 牌型
enum EPokerHandType : int
{
    kInvalid = 0,  // 无效类型
    kRoyalFlush = 1,  // 皇家同花顺
    kStraigthFlush = 2, // 同花顺
    kFourOfAKind = 3, // 金刚
    kFullHouse = 4,  // 葫芦
    kFlush = 5,  // 同花
    kStraigth = 6,  // 顺子
    kThreeOfAKind = 7,  // 三条
    kTwoPair = 8,  // 两队
    kOnePair = 9,  // 一对
    kHighCard = 10,  // 高牌
};

// @brief 获取指定花色
inline int get_color(int card)
{
    return card >> 8;
}

// @brief 获取牌值
inline int get_value(int card)
{
    return card & 0x00ff;
}

// @brief 比较两张扑克:  left < right
inline bool compare_card_lesser(int left, int right)
{
    auto left_value = get_value(left);
    auto right_value = get_value(right);
    if (left_value < right_value)
        return true;
    else
    {
        if (left_value == right_value)
        {
            if (get_color(left) < get_color(right))
                return true;
        }

    }
    return false;
}


// @brief 牌型分析辅助结构
class CardsAnalazyInfo
{
public:
    CardsAnalazyInfo() {}

    void clear()
    {
        _is_straigth = false;
        _is_royal_flush = false;
        _is_flush = false;
        _two_cnt = 0;
        _three_cnt = 0;
        _four_cnt = 0;
        _cards.clear();
    }

    void analaze(const std::vector<int>& cards)
    {
        if (cards.size() != 5)
            return;
        clear();
        _cards = cards;
        std::sort(_cards.begin(), _cards.end(), compare_card_lesser);
        // 计算万能牌数量
        int wild_cnt = std::count_if(_cards.begin(), _cards.end(), [](const int card)->bool{return get_color(card) > ECardColor::kDiamond; });
        _wild_cnt = wild_cnt;
        int none_wild_cnt = _cards.size() - wild_cnt;
        int line_cnt = 1;  // 记录值连续张数
        int max_line_cnt = 1;  // 排除万能牌后的最大连续数量
        int max_line_start_card = 0;  // 最大顺子的第一张
        int final_line_start_card_value = 0;  // 最终如果是顺子，顺子的第一张逻辑值
        int same_flush_cnt = 1;
        int same_value_cnt = 1;
        // 遍历非万能牌
        for (std::size_t idx = 1; idx < none_wild_cnt; ++idx)
        {
            // 牌值连续检查
            if (get_value(_cards[idx-1]) + 1 == get_value(_cards[idx]))
            {
                line_cnt++;
                if (line_cnt > max_line_cnt)
                {
                    max_line_cnt = line_cnt;
                    max_line_start_card = _cards[idx + 1 - max_line_cnt];
                }
            }
            else
            {
                if (get_value(max_line_start_card) == 0x02 && (idx == none_wild_cnt - 1) && get_value(_cards[idx]) == 0x0e)
                {
                    line_cnt++;
                    if (line_cnt > max_line_cnt)
                    {
                        max_line_cnt = line_cnt;
                        max_line_start_card = _cards[idx + 1 - max_line_cnt];
                    }
                }
                line_cnt = 1;
            }
            // 花色相同检查
            if (get_color(_cards[idx-1]) == get_color(_cards[idx]))
                same_flush_cnt++;
            // 牌值相同检查
            if (get_value(_cards[idx-1]) == get_value(_cards[idx]))
                same_value_cnt++;
            else
            {
                switch (same_value_cnt)
                {
                case 2:
                    _two_cnt++;
                    break;
                case 3:
                    _three_cnt++;
                    break;
                case 4:
                    _four_cnt++;
                    break;
                }
                same_value_cnt = 1;
            }
        }
        // 最后一次same_value统计
        switch (same_value_cnt)
        {
        case 2:
            _two_cnt++;
            break;
        case 3:
            _three_cnt++;
            break;
        case 4:
            _four_cnt++;
            break;
        }
        // 同花判定
        int flush_need_wild_cnt = 5 - same_flush_cnt;
        if (wild_cnt >= flush_need_wild_cnt)
            _is_flush = true;
        // 顺子和皇家同花顺判定
        int line_need_wild_cnt = 0;
        if (max_line_cnt < 5)
        {
            line_cnt = 1;
            int last_value = 0;
            for (std::size_t idx = 0; idx < none_wild_cnt - 1;)
            {
                if (idx == 0 && line_need_wild_cnt == 0)
                {
                    last_value = get_value(_cards[idx]);
                    final_line_start_card_value = last_value;
                }
                if (last_value + 1 != get_value(_cards[idx+1]))
                {
                    // 处理A2345
                    if ((idx == (none_wild_cnt - 1 - 1)) && get_value(max_line_start_card) == 0x02 && get_value(_cards[idx+1]) == 0x0e)
                    {
                        ++idx;
                        last_value = 0x0e;
                        final_line_start_card_value = 0x0e;
                        continue;
                    }
                    else
                    {
                        line_need_wild_cnt++;
                        line_cnt++;
                        last_value++;
                        // 提前判定失败不可能是顺子
                        if (line_need_wild_cnt > wild_cnt)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    line_cnt++;
                    last_value += 1;
                    ++idx;
                }
           }
            // 需要额外补充wild
            if (line_cnt < 5)
            {
                int ext_wild_cnt = 5 - line_cnt;  // 需要额外添加的万能牌
                line_need_wild_cnt += ext_wild_cnt;
                if (final_line_start_card_value + line_cnt - 1 + ext_wild_cnt > 0x0e)
                    final_line_start_card_value -= (final_line_start_card_value + line_cnt - 1 + ext_wild_cnt - 0x0e);
            }
            // 如果需要的万能牌数量足够，认定是顺子
            if (line_need_wild_cnt <= wild_cnt)
            {
                _is_straigth = true;
            }
        }
        else
        {
            _is_straigth = true;
            final_line_start_card_value = get_value(max_line_start_card);
        }
        // 同花且顺子，而且同花要求的万能牌小于等于顺子认定同花顺
        if (_is_flush && _is_straigth && line_need_wild_cnt >= flush_need_wild_cnt)
        {
            _is_straigth_flush = true;
            // 10开始的才能是皇家同花顺
            if (final_line_start_card_value == 0x0a)
                _is_royal_flush = true;
        }
    }

    int get_hand_card_type()
    {
        if (_is_royal_flush)
            return EPokerHandType::kRoyalFlush;
        if (_is_straigth_flush)
            return EPokerHandType::kStraigthFlush;
        if (_four_cnt || _three_cnt + _wild_cnt >= 4 || _three_cnt + _wild_cnt >= 4)
        {
            return EPokerHandType::kFourOfAKind;
        }
        if ((_three_cnt && _two_cnt) ||
            (_two_cnt >= 2 && _wild_cnt >= 1) ||
            (_two_cnt >= 1 && _wild_cnt >= 2))
            return EPokerHandType::kFullHouse;
        if (_is_flush)
            return EPokerHandType::kFlush;
        if (_is_straigth)
            return EPokerHandType::kStraigth;
        if (_three_cnt ||
            (_two_cnt >= 1 && _wild_cnt >=1) ||
            (_wild_cnt >= 2))
            return EPokerHandType::kThreeOfAKind;
        if (_two_cnt == 2 || _wild_cnt >= 1)
            return EPokerHandType::kTwoPair;
        if (_two_cnt == 1)
            return EPokerHandType::kOnePair;
        return EPokerHandType::kHighCard;
    }

protected:
    int _wild_cnt = 0;                          // 万能牌数量
    bool _is_straigth = false;                  // 是否顺子
    bool _is_royal_flush = false;               // 是否皇家同花顺
    bool _is_straigth_flush = false;            // 同花顺
    bool _is_flush = false;                     // 是否同花
    int _two_cnt = 0;                           // 两张一样的数量
    int _three_cnt = 0;                         // 三张一样的数量
    int _four_cnt = 0;                          // 四张一样的数量
    std::vector<int> _cards;                    // 排序好的原始牌组
};

// @brief 牌型判定
int get_hand_card_type_v2(const std::vector<int>& input)
{
    if (input.size() != 5)
        return EPokerHandType::kInvalid;
    CardsAnalazyInfo analazy_info;
    analazy_info.analaze(input);
    return analazy_info.get_hand_card_type();
}


// @brief 不含万能牌的用例
bool testcase1_1()
{
    std::vector<int> hand_card_1{0x10a, 0x10b, 0x10c, 0x10d, 0x10e};
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(hand_card_1));

    std::vector<int> hand_card_2{0x104, 0x105, 0x106, 0x107, 0x108};
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(hand_card_2));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x109,0x10a,0x10b,0x10c,0x10d}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x10e,0x102,0x103,0x104,0x105}));

    std::vector<int> hand_card_3{0x103, 0x203, 0x303, 0x403, 0x207};
    assert(EPokerHandType::kFourOfAKind == get_hand_card_type_v2(hand_card_3));
    std::vector<int> hand_card_4{0x105, 0x205, 0x305, 0x409, 0x209};
    assert(EPokerHandType::kFullHouse == get_hand_card_type_v2(hand_card_4));
    std::vector<int> hand_card_5{0x108, 0x208, 0x308, 0x102, 0x303};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type_v2(hand_card_5));
    std::vector<int> hand_card_6{0x105, 0x206, 0x307, 0x408, 0x309};
    assert(EPokerHandType::kStraigth == get_hand_card_type_v2(hand_card_6));
    std::vector<int> hand_card_7{0x106, 0x206, 0x306, 0x408, 0x209};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type_v2(hand_card_7));
    std::vector<int> hand_card_8{0x106, 0x206, 0x308, 0x408, 0x209};
    assert(EPokerHandType::kTwoPair == get_hand_card_type_v2(hand_card_8));
    std::vector<int> hand_card_9{0x103, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kOnePair == get_hand_card_type_v2(hand_card_9));
    std::vector<int> hand_card_10{0x10a, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kHighCard == get_hand_card_type_v2(hand_card_10));

    return true;
}

bool testcase1_2()
{
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x10a, 0x10b, 0x10c, 0x10d, 0x10e}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x50f, 0x10b, 0x10c, 0x10d, 0x10e}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x50f, 0x610, 0x10c, 0x10d, 0x10e}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x10a, 0x610, 0x10c, 0x10d, 0x10e}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x10a, 0x610, 0x50f, 0x10d, 0x10e}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x10a, 0x10b, 0x10c, 0x10d, 0x50f}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x10a, 0x10b, 0x10c, 0x610, 0x50f}));
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(std::vector<int>{0x10a, 0x610, 0x10c, 0x50f, 0x10e}));
    assert(EPokerHandType::kRoyalFlush != get_hand_card_type_v2(std::vector<int>{0x109, 0x610, 0x10c, 0x50f, 0x10e}));

    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x104, 0x105, 0x106, 0x107, 0x108}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x50f, 0x105, 0x106, 0x107, 0x108}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x50f, 0x610, 0x106, 0x107, 0x108}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x50f, 0x105, 0x610, 0x107, 0x108}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x104, 0x50f, 0x610, 0x107, 0x108}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x104, 0x50f, 0x610, 0x107, 0x108}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x104, 0x105, 0x106, 0x107, 0x50f}));
    assert(EPokerHandType::kStraigthFlush != get_hand_card_type_v2(std::vector<int>{0x10c, 0x105, 0x106, 0x107, 0x50f}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x109,0x10a,0x10b,0x10c,0x10d}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x10e,0x102,0x103,0x104,0x105}));

    std::vector<int> hand_card_3{0x103, 0x203, 0x303, 0x403, 0x207};
    assert(EPokerHandType::kFourOfAKind == get_hand_card_type_v2(hand_card_3));
    std::vector<int> hand_card_4{0x105, 0x205, 0x305, 0x409, 0x209};
    assert(EPokerHandType::kFullHouse == get_hand_card_type_v2(hand_card_4));
    std::vector<int> hand_card_5{0x108, 0x208, 0x308, 0x102, 0x303};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type_v2(hand_card_5));
    std::vector<int> hand_card_6{0x105, 0x206, 0x307, 0x408, 0x309};
    assert(EPokerHandType::kStraigth == get_hand_card_type_v2(hand_card_6));
    std::vector<int> hand_card_7{0x106, 0x206, 0x306, 0x408, 0x209};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type_v2(hand_card_7));
    std::vector<int> hand_card_8{0x106, 0x206, 0x308, 0x408, 0x209};
    assert(EPokerHandType::kTwoPair == get_hand_card_type_v2(hand_card_8));
    std::vector<int> hand_card_9{0x103, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kOnePair == get_hand_card_type_v2(hand_card_9));
    std::vector<int> hand_card_10{0x10a, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kHighCard == get_hand_card_type_v2(hand_card_10));

    return true;
}

int main()
{
    //testcase1_1();
    testcase1_2();
    return 0;
}